package org.zjvis.datascience.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.zjvis.datascience.common.constant.NoticeConstant;
import org.zjvis.datascience.common.constant.ProjectConstant;
import org.zjvis.datascience.common.dto.DatasetProjectDTO;
import org.zjvis.datascience.common.dto.PipelineDTO;
import org.zjvis.datascience.common.dto.ProjectDTO;
import org.zjvis.datascience.common.dto.ProjectStatusDTO;
import org.zjvis.datascience.common.dto.RoleDTO;
import org.zjvis.datascience.common.dto.UserProjectDTO;
import org.zjvis.datascience.common.widget.dto.WidgetFavouriteDTO;
import org.zjvis.datascience.common.dto.user.UserDTO;
import org.zjvis.datascience.common.enums.NoticeTypeEnum;
import org.zjvis.datascience.common.enums.ProjectAuthEnum;
import org.zjvis.datascience.common.enums.ProjectRoleAuthEnum;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.util.RedisUtil;
import org.zjvis.datascience.common.util.StringUtil;
import org.zjvis.datascience.common.vo.project.BatchCreateUserProjectVO;
import org.zjvis.datascience.common.vo.project.CreateUserProjectVO;
import org.zjvis.datascience.common.vo.project.LoadDatasetVO;
import org.zjvis.datascience.common.vo.project.UserProjectVO;
import org.zjvis.datascience.service.mapper.DashboardMapper;
import org.zjvis.datascience.service.mapper.RoleMapper;
import org.zjvis.datascience.service.mapper.UserProjectMapper;
import com.alibaba.fastjson.JSONObject;

/**
 * @description UserProject 用户项目 Service
 * @date 2021-12-26
 */
@Service
public class UserProjectService {

    private final static Logger logger = LoggerFactory.getLogger("UserProjectService");

    @Autowired
    private UserProjectMapper userProjectMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Lazy
    @Autowired
    private ProjectService projectService;

    @Autowired
    private UserService userService;

    @Autowired
    private DatasetProjectService datasetProjectService;

    @Autowired
    private PipelineService pipelineService;

    @Autowired
    private WidgetFavouriteService widgetFavouriteService;

    @Autowired
    private WidgetService widgetService;

    @Autowired
    private JlabService jlabService;

    @Autowired
    private RedisUtil redisUtil;

    @Lazy
    @Autowired
    private NoticeService noticeService;

    @Autowired
    private DashboardMapper dashboardMapper;


    @Transactional(rollbackFor = Exception.class)
    public long saveProject(ProjectDTO dto) {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        //添加一条project表数据
        long id = projectService.save(dto);
        //添加一条user_project表数据
        save(id);
        int count = projectService.countExistPipeline(id, user.getId());
        //添加一条pipeline数据
        PipelineDTO pd = PipelineDTO.builder().name(generatePipelineName(count, user.getName())).userId(user.getId())
                .projectId(id).dataJson("{\"key\":\"value\"}").build();
        pipelineService.save(pd);
        return id;
    }

    private String generatePipelineName(int existPipelineNum, String userName) {
        if (existPipelineNum <= 1){
            return userName + "'s pipeline";
        }else {
            return userName + "'s pipeline (" + (existPipelineNum - 1) + ")";
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteProject(ProjectDTO dto) {
        if (!projectService.exist(dto.getId())) {
            throw DataScienceException.of(BaseErrorCode.PROJECT_NOT_EXIST,"");
        }
        //删除的时候检查是不是有已经发布的dashboard, 如果有则拒绝删除
        if (dashboardMapper.countPublishedNum(dto.getId()) > 0){
            throw DataScienceException.of(BaseErrorCode.DASHBOARD_PUBLISHED_IN_PROJECT,"");
        }
        doDeleteProject(dto.getId());
    }

    public List<UserProjectDTO> query(long projectId) {
        List<UserProjectDTO> upds = userProjectMapper
            .selectWithUserName(UserProjectDTO.builder().projectId(projectId).build());
        if (upds != null) {
            List<RoleDTO> roles = roleMapper.listAll();
            List<UserProjectDTO> res = new ArrayList<>();
            for (UserProjectDTO upd : upds) {
                if (roles != null) {
                    for (RoleDTO role : roles) {
                        if (role.getId() != null && role.getId().equals(upd.getRoleId())) {
                            upd.setRoleName(role.getName());
                            break;
                        }
                    }
                }
                res.add(upd);
            }
            return res;
        }

        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public int delete(long id) {
        UserProjectDTO dto = userProjectMapper.selectByPrimaryKey(id);
        if (dto == null) {
            return 0;
        }
        //防止管理员互删及管理员删除创建者
        checkRoleOperationAuth(dto.getProjectId(), dto.getUserId(), dto.getRoleId());

        //TODO 删除或改动
        //checkDatasetUsedInProject(dto.getProjectId(), dto.getUserId());

        //获取project信息
        ProjectDTO pd = projectService.query(dto.getProjectId());
        if (pd == null) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_NOT_EXIST, "项目id:" + dto.getProjectId());
        }

        UserDTO currentUser = JwtUtil.getCurrentUserDTO();

        int res = userProjectMapper.deleteByPrimaryKey(id);
        if (res > 0) {
            datasetProjectService.deleteByProjectAndUser(dto.getProjectId(), dto.getUserId());
            String key = String
                .format(ProjectConstant.PROJECT_USER_ROLE_KEY, dto.getProjectId(), dto.getUserId());

            redisUtil.del(key);

            //删除的角色高于访客时，删除掉对应的pipeline和task
            if (dto.getRoleId() < ProjectRoleAuthEnum.VISITOR.getValue()) {
                PipelineDTO pipeline = pipelineService
                    .queryByProjectAndUser(dto.getProjectId(), dto.getUserId());
                if (pipeline != null) {
                    //删除pipeline中的task
                    pipelineService.deleteTasksByPipeline(pipeline.getId());
                    //删除pipeline表中的记录
                    pipelineService.delete(pipeline.getId());
                }
            }

            String title = String
                .format(NoticeConstant.PROJECT_USER_ROLE_DELETE_TITLE, pd.getName());
            String content = String
                .format(NoticeConstant.PROJECT_USER_ROLE_DELETE_CONTENT,
                    StringUtil.addHtmlBoldLabel("您"),
                    StringUtil.addHtmlBoldLabel(currentUser.getName()),
                    StringUtil.addHtmlBoldLabel(pd.getName()));
            String process = JSONObject.toJSONString(dto);
            //发送给移除角色的用户
            noticeService.saveAndSendToUser(dto.getUserId(), title, content, process,
                NoticeTypeEnum.system.getType(), currentUser.getId());

            Set<Long> receivers = getReceiver(dto.getProjectId(),
                new HashSet<Long>() {{
                    add(JwtUtil.getCurrentUserId());
                    add(dto.getUserId());
                }},
                new HashSet<Integer>() {{
                    add(ProjectRoleAuthEnum.VISITOR.getValue());
                    add(ProjectRoleAuthEnum.DEVELOPER.getValue());
                }});
            UserDTO user = userService.queryById(dto.getUserId());
            content = String.format(NoticeConstant.PROJECT_USER_ROLE_DELETE_CONTENT,
                StringUtil.addHtmlBoldLabel(user.getName()),
                StringUtil.addHtmlBoldLabel(currentUser.getName()),
                StringUtil.addHtmlBoldLabel(pd.getName()));
            //给除自己外的项目管理人员发项目级别通知消息;
            noticeService.saveAndSendToUser(receivers, title, content, process,
                NoticeTypeEnum.project.getType(), JwtUtil.getCurrentUserId());

            logger.info(currentUser.getName() + "将用户" + dto.getUserId() + " 移出" + dto.getProjectId()
                + "项目");
        }

        return res;
    }


    public void create2(BatchCreateUserProjectVO vo) {
        List<UserProjectDTO> dtos = vo.getUserRoles().stream().map(i -> {
            UserDTO user = userService.checkValidUser2(i.getUserId());

            return CreateUserProjectVO.toDto(i, vo.getProjectId(), user);
        }).collect(Collectors.toList());

        if (dtos == null || dtos.isEmpty()) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_ADD_USER_NOT_EXIT, "项目id:" + vo.getProjectId());
        }

        int count = userProjectMapper.getOperationalUserCount(vo.getProjectId());

        //判断已有用户是否达到阈值
        if (count >= ProjectConstant.PROJECT_OPERATIONAL_USER) {
            throw DataScienceException.of(BaseErrorCode.PROJECT_OPERATIONAL_USER_EXCEEDS_THRESHOLD,
                "项目id:" + vo.getProjectId());
        } else if ((count + dtos.size()) > ProjectConstant.PROJECT_OPERATIONAL_USER) {
            throw DataScienceException.of(BaseErrorCode.PROJECT_OPERATIONAL_USER_EXCEEDS_THRESHOLD,
                "项目id:" + vo.getProjectId());
        } else {
            try {
                userProjectMapper.batchInsert(dtos);
                for (UserProjectDTO dto : dtos) {
                    //添加的角色高于访客时，为添加的用户创建pipeline
                    if (dto.getRoleId() < ProjectRoleAuthEnum.VISITOR.getValue()) {
                        PipelineDTO pd = PipelineDTO.builder().name(dto.getUserName())
                            .userId(dto.getUserId())
                            .projectId(dto.getProjectId())
                            .dataJson("{\"key\":\"value\"}").build();
                        pipelineService.save(pd);
                    }
                }
            } catch (DuplicateKeyException e) {
                throw DataScienceException
                    .of(BaseErrorCode.PROJECT_AUTH_DUPLICATE_ERROR, e.getCause().getMessage());
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void create(BatchCreateUserProjectVO vo) {
        List<UserProjectDTO> dtos = vo.getUserRoles().stream().map(i -> {
            UserDTO user = userService.checkValidUser2(i.getUserId());
            return CreateUserProjectVO.toDto(i, vo.getProjectId(), user);
        }).collect(Collectors.toList());

        for (UserProjectDTO upd : dtos) {
            checkRoleOperationAuth(upd.getProjectId(), upd.getUserId(), upd.getRoleId());
        }

        if (dtos == null || dtos.isEmpty()) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_ADD_USER_NOT_EXIT, "项目id:" + vo.getProjectId());
        }

        int count = userProjectMapper.getOperationalUserCount(vo.getProjectId());

        //判断已有用户是否达到阈值
        int size = 0;
        for (UserProjectDTO upd : dtos) {
            //比访客权限高时
            if (upd.getRoleId() < 4) {
                size += 1;
            }
        }
        //列表中添加的角色有高于访客时
        if (size > 0) {
            checkThreshold(count + size, vo.getProjectId());
        }

        //获取project信息
        ProjectDTO pd = projectService.query(vo.getProjectId());
        if (pd == null) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_NOT_EXIST, "项目id:" + vo.getProjectId());
        }

        UserDTO currentUser = JwtUtil.getCurrentUserDTO();

        long currentUserId = JwtUtil.getCurrentUserId();

        try {
            //保存数据到user_project表
            userProjectMapper.batchInsert(dtos);
            List<RoleDTO> roles = roleMapper.list();

            for (UserProjectDTO dto : dtos) {
                //添加的角色高于访客时，为添加的用户创建pipeline
                if (dto.getRoleId() < ProjectRoleAuthEnum.VISITOR.getValue()) {
                    PipelineDTO pipeline = PipelineDTO.builder().name(dto.getUserName())
                        .userId(dto.getUserId())
                        .projectId(dto.getProjectId())
                        .dataJson("{\"key\":\"value\"}").build();
                    pipelineService.save(pipeline);
                }

                //添加一条记录到消息表
                String roleName = "";
                for (RoleDTO role : roles) {
                    if (role.getId().equals(dto.getRoleId())) {
                        roleName = role.getName();
                        break;
                    }
                }

                //发送socket通知
                String title = String
                    .format(NoticeConstant.PROJECT_USER_ROLE_ADD_TITLE, pd.getName());
                String content = String.format(NoticeConstant.PROJECT_USER_ROLE_ADD_CONTENT,
                    StringUtil.addHtmlBoldLabel("您"),
                    StringUtil.addHtmlBoldLabel(currentUser.getName()),
                    StringUtil.addHtmlBoldLabel(roleName),
                    StringUtil.addHtmlBoldLabel(pd.getName()));
                String process = JSONObject.toJSONString(dto);
                //给被添加人发送系统消息
                noticeService.saveAndSendToUser(dto.getUserId(), title, content, process,
                    NoticeTypeEnum.system.getType(), currentUserId);

                Set<Long> receivers = getReceiver(vo.getProjectId(),
                    new HashSet<Long>() {{
                        add(JwtUtil.getCurrentUserId());
                        add(dto.getUserId());
                    }},
                    new HashSet<Integer>() {{
                        add(ProjectRoleAuthEnum.VISITOR.getValue());
                        add(ProjectRoleAuthEnum.DEVELOPER.getValue());
                    }});
                UserDTO user = userService.queryById(dto.getUserId());
                content = String
                    .format(NoticeConstant.PROJECT_USER_ROLE_ADD_CONTENT,
                        StringUtil.addHtmlBoldLabel(user.getName()),
                        StringUtil.addHtmlBoldLabel(currentUser.getName()),
                        StringUtil.addHtmlBoldLabel(roleName),
                        StringUtil.addHtmlBoldLabel(pd.getName()));
                //给除自己外的项目管理人员发项目级别通知消息
                noticeService.saveAndSendToUser(receivers, title, content, process,
                    NoticeTypeEnum.project.getType(), currentUserId);

                logger.info(content);
            }
        } catch (DuplicateKeyException e) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_AUTH_DUPLICATE_ERROR, e.getCause().getMessage());
        }
    }

    /**
     * 检查阈值
     *
     * @param addSize
     * @param projectId
     */
    public void checkThreshold(int addSize, Long projectId) {
        int count = userProjectMapper.getOperationalUserCount(projectId);
        if (count >= ProjectConstant.PROJECT_OPERATIONAL_USER ||
            (count + addSize) > ProjectConstant.PROJECT_OPERATIONAL_USER) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_OPERATIONAL_USER_EXCEEDS_THRESHOLD, "项目id:" + projectId);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addUserRoleAndCreatePipeline(UserProjectDTO dto) {
        //检查阈值
        checkThreshold(1, dto.getProjectId());

        try {
            //添加user_project表信息
            userProjectMapper.insert(dto);
            //添加的角色高于访客时，为添加的用户创建pipeline
            if (dto.getRoleId() < ProjectRoleAuthEnum.VISITOR.getValue()) {
                PipelineDTO pd = PipelineDTO.builder().name(dto.getUserName())
                    .userId(dto.getUserId())
                    .projectId(dto.getProjectId())
                    .dataJson("{\"key\":\"value\"}").build();
                pipelineService.save(pd);
            }
        } catch (DuplicateKeyException e) {
            if (e.getCause().getMessage().startsWith("Duplicate entry")) {
                throw DataScienceException
                    .of(BaseErrorCode.PROJECT_AUTH_EXIST_ERROR, "项目id：" + dto.getProjectId());
            } else {
                throw DataScienceException.of(BaseErrorCode.ERROR, e.getCause().getMessage());
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(UserProjectVO vo) {
        //防止同级修改
        checkRoleOperationAuth(vo.getProjectId(), vo.getUserId(), vo.getRoleId());

        UserProjectDTO dto = UserProjectDTO.builder().projectId(vo.getProjectId())
            .userId(vo.getUserId()).roleId(vo.getRoleId()).build();

        //获取project信息
        ProjectDTO project = projectService.query(vo.getProjectId());
        if (project == null) {
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_NOT_EXIST, "项目id:" + vo.getProjectId());
        }

        try {
            Integer oldRoleId = userProjectMapper.getRoleId(vo.getUserId(), vo.getProjectId());
            if (vo.getRoleId().equals(oldRoleId)) {
                return;
            }
            int res = userProjectMapper.updateUserProjectRole(dto);

            //有新的变动
            if (res > 0) {
                long currentUserId = JwtUtil.getCurrentUserId();
                String key = String
                    .format(ProjectConstant.PROJECT_USER_ROLE_KEY, dto.getProjectId(),
                        dto.getUserId());
                redisUtil.del(key);

                //当角色更改为访客时，删除掉对应的pipeline相关数据
                if (ProjectRoleAuthEnum.VISITOR.getValue().equals(dto.getRoleId())) {
                    PipelineDTO pipeline = pipelineService
                        .queryByProjectAndUser(dto.getProjectId(), dto.getUserId());
                    if (pipeline != null) {
                        //删除pipeline中的task
                        pipelineService.deleteTasksByPipeline(pipeline.getId());
                        //删除pipeline表中的记录
                        pipelineService.delete(pipeline.getId());
                    }
                } else if (!pipelineService.pipelineExists(dto.getProjectId(), dto.getUserId())) {
                    //获取用户信息
                    UserDTO user = userService.queryById(dto.getUserId());
                    if (user != null) {
                        PipelineDTO pd = PipelineDTO.builder().name(user.getName())
                            .userId(dto.getUserId())
                            .projectId(dto.getProjectId())
                            .dataJson("{\"key\":\"value\"}").build();
                        pipelineService.save(pd);
                    }
                }

                //添加一条记录到消息
                RoleDTO oldRole = roleMapper.getRoleById(oldRoleId);
                String oldRoleName = oldRole == null ? "" : oldRole.getName();
                RoleDTO newRole = roleMapper.getRoleById(vo.getRoleId());
                String newRoleName = newRole == null ? "" : newRole.getName();
                String title = String
                    .format(NoticeConstant.PROJECT_USER_ROLE_CHANGE_TITLE, project.getName());
                String content = String
                    .format(NoticeConstant.PROJECT_USER_ROLE_CHANGE_CONTENT,
                        StringUtil.addHtmlBoldLabel("您"),
                        StringUtil.addHtmlBoldLabel(project.getName()),
                        StringUtil.addHtmlBoldLabel(oldRoleName),
                        StringUtil.addHtmlBoldLabel(newRoleName));
                String process = JSONObject.toJSONString(vo);
                //给被修改用户发消息
                noticeService.saveAndSendToUser(dto.getUserId(), title, content, process,
                    NoticeTypeEnum.project.getType(), currentUserId);

                Set<Long> receivers = getReceiver(vo.getProjectId(),
                    new HashSet<Long>() {{
                        add(JwtUtil.getCurrentUserId());
                        add(dto.getUserId());
                    }},
                    new HashSet<Integer>() {{
                        add(ProjectRoleAuthEnum.VISITOR.getValue());
                        add(ProjectRoleAuthEnum.DEVELOPER.getValue());
                    }});
                UserDTO user = userService.queryById(dto.getUserId());
                content = String
                    .format(NoticeConstant.PROJECT_USER_ROLE_CHANGE_CONTENT,
                        StringUtil.addHtmlBoldLabel(user.getName()),
                        StringUtil.addHtmlBoldLabel(project.getName()),
                        StringUtil.addHtmlBoldLabel(oldRoleName),
                        StringUtil.addHtmlBoldLabel(newRoleName));
                //给除自己外的项目管理人员发项目级别通知消息
                noticeService.saveAndSendToUser(receivers, title, content, process,
                    NoticeTypeEnum.project.getType(), JwtUtil.getCurrentUserId());

                logger.info(content);
            }
        } catch (DuplicateKeyException e) {
            throw new DataScienceException(BaseErrorCode.PROJECT_AUTH_EXIST_ERROR);
        }
    }

    public ProjectDTO queryProject(ProjectDTO dto) {
        ProjectDTO res = projectService.query(dto.getId());
        if (res == null) {
            return null;
        }
        UserProjectDTO rel = queryCurrentUser(dto.getId());
        //res.setAuth(rel.getAuth());
        return res;
    }

    public UserProjectDTO queryCurrentUser(long projectId) {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        List<UserProjectDTO> dtos = userProjectMapper.select(UserProjectDTO.builder()
            .projectId(projectId).userId(user.getId())
            .build());
        if (CollectionUtils.isEmpty(dtos)) {
            return null;
        }
        return dtos.get(0);
    }

    private void doDeleteProject(long id) {
        datasetProjectService.delete(LoadDatasetVO.builder().projectId(id).build());
        pipelineService.deleteByProject(id);
        userProjectMapper.delete(UserProjectDTO.builder().projectId(id).build());
        int res = projectService.delete(id);
        if (res > 0) {
            logger.info("project删除时，清除缓存：" + String.format(ProjectConstant.PROJECT_STATUS_KEY, id));
            //前置修改成功，删除缓存中的状态值
            redisUtil.del(String.format(ProjectConstant.PROJECT_STATUS_KEY, id));
        }
    }

    public void save(long projectId) {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        UserProjectDTO userProject = UserProjectDTO.builder()
            .userId(user.getId())
            .projectId(projectId)
            .auth(ProjectAuthEnum.ADMIN.getValue())
            .roleId(ProjectRoleAuthEnum.CREATOR.getValue()).build();
        userProjectMapper.insertSelective(userProject);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateProject(ProjectDTO dto) {
        ProjectDTO project = projectService.query(dto.getId());
        String oldName = project.getName();
        if (dto.getName() != null && !oldName.equals(dto.getName())){
            jlabService.renameProjectDir(project, dto.getName());
        }
        try {
            projectService.update(dto);
        } catch (DuplicateKeyException e) {
            throw new DataScienceException(BaseErrorCode.PROJECT_NAME_DUPLICATE_ERROR);
        }
    }

    public void favouriteWidget(WidgetFavouriteDTO dto) {
        widgetAndPipelineCheck(dto);
        widgetFavouriteService.favourite(dto);
    }

    public void favouriteWidgetCancel(WidgetFavouriteDTO dto) {
        widgetAndPipelineCheck(dto);
        widgetFavouriteService.favouriteCancel(dto);
    }

    public List<WidgetFavouriteDTO> queryFavouriteWidget(long pipelineId) {
        checkPipelineRead(pipelineId);
        return widgetFavouriteService.query(pipelineId);
    }

    private void widgetAndPipelineCheck(WidgetFavouriteDTO dto) {
        widgetService.check(dto.getWidgetId());
        checkPipelineWrite(dto.getPipelineId());
    }

    public void checkPipelineWrite(long id) {
        PipelineDTO dto = pipelineService.checkExist(id);
        checkWriteAuth(dto.getProjectId());
    }

    public void checkPipelineRead(long id) {
        PipelineDTO dto = pipelineService.checkExist(id);
        checkReadAuth(dto.getProjectId());
    }

    /**
     * 判断当前登录用户是否有该项目的管理员权限
     */
    public void checkAdminAuth(long projectId) {
        checkAuth(projectId, ProjectAuthEnum.ADMIN.getValue());
    }

    public void checkWriteAuth(long projectId) {
        checkAuth(projectId, ProjectAuthEnum.WRITE.getValue());
    }

    public void checkReadAuth(long projectId) {
        checkAuth(projectId, ProjectAuthEnum.READ.getValue());
    }

    public void checkAuth(long projectId, byte auth) {
        //TODO 旧有逻辑验证，暂时去掉
//        UserDTO user = JwtUtil.getCurrentUserDTO();
//        if (mapper.haveAuthority(user.getId(), projectId, auth) == 0) {
//            throw DataScienceException.of(BaseErrorCode.UNAUTHORIZED, "项目id:" + projectId);
//        }
    }

    public List<RoleDTO> listRole() {
        List<RoleDTO> list = roleMapper.list();
        return list;
    }

    public void checkRoleAuth(Long projectId, Integer authRoleId, boolean checkLock, Long userId) {
        //checkProjectStatus(projectId,authRoleId,checkLock);

        ProjectStatusDTO psd = getProjectStatus(projectId);
        //TODO 兼容旧版过段时间删掉
        if (psd.getId() == null) {
            psd.setId(projectId);
        }

//        checkProjectStatus(psd, authRoleId, checkLock);

        //模板项目，不进行后续校验
        if (psd.getType() == 1) {
            return;
        }

        Integer roleId = getCurrentUserRole(projectId, userId);
        if (null == roleId || roleId<ProjectRoleAuthEnum.CREATOR.getValue()) {
            throw DataScienceException.of(BaseErrorCode.UNAUTHORIZED, "项目id:" + projectId);
        } else if (roleId>authRoleId) {
          if (null == roleId || roleId < ProjectRoleAuthEnum.CREATOR.getValue()) {
              throw DataScienceException.of(BaseErrorCode.UNAUTHORIZED, "项目id:" + projectId);
          } else if (roleId > authRoleId) {
              throw DataScienceException.of(BaseErrorCode.PROJECT_NO_AUTH, "项目id:" + projectId);
          }
        }
    }

    /**
     * 获取project状态
     *
     * @param projectId
     * @return
     */
    private ProjectStatusDTO getProjectStatus(Long projectId) {
        String key = String.format(ProjectConstant.PROJECT_STATUS_KEY, projectId);
        ProjectStatusDTO psd = (ProjectStatusDTO) redisUtil.get(key);
        if (psd == null) {
            psd = projectService.getProjectStatus(projectId);
            if (psd == null) {
                throw DataScienceException.of(BaseErrorCode.PROJECT_NOT_EXIST, "项目id:" + projectId);
            } else {
                redisUtil.set(key, psd, 3600);
            }
        }
        return psd;
    }

    /**
     * 获取操作用户在project中的角色
     *
     * @param projectId
     * @return
     */
    public Integer getCurrentUserRole(Long projectId, Long userId) {
        return getUserRole(projectId, userId);
    }

    /**
     * 获取指定用户在指定项目中的角色
     *
     * @param projectId
     * @param userId
     * @return
     */
    public Integer getUserRole(Long projectId, Long userId) {

        String key = String.format(ProjectConstant.PROJECT_USER_ROLE_KEY, projectId, userId);

        Integer roleId = (Integer) redisUtil.get(key);
        if (roleId == null) {
            roleId = userProjectMapper.getRoleId(userId, projectId);
            if (roleId != null) {
                redisUtil.set(key, roleId, 3600);
            }
        }

        return roleId;
    }

    public void checkRoleOperationAuth(Long projectId, Long authUserId, Integer newRoleId) {
        
        Integer currentUserRoleId = getCurrentUserRole(projectId, JwtUtil.getCurrentUserId());
        Integer oldRoleId = getUserRole(projectId, authUserId);

        /**
         * 1.创建者 2.项目管理员 3.项目开发者 4.项目访客
         */
        if (null == currentUserRoleId) {
            throw DataScienceException.of(BaseErrorCode.UNAUTHORIZED, "项目id:" + projectId);
        } else if ((oldRoleId != null && currentUserRoleId >= oldRoleId)
            || currentUserRoleId >= newRoleId) {
            // 只能操作角色比自己低的 用户
            throw DataScienceException
                .of(BaseErrorCode.PROJECT_AUTH_PERMISSION_DENIED, "项目id:" + projectId);
        }
        if (null == oldRoleId) {
            //添加角色
            return;
        } else if (oldRoleId < ProjectRoleAuthEnum.VISITOR.getValue()
            && newRoleId > ProjectRoleAuthEnum.DEVELOPER.getValue()) {
            //用户角色降级为访客时
            //TODO 删除或改动
            //checkDatasetUsedInProject(projectId, authUserId);
        }
    }

    /**
     * 验证项目状态
     *
     * @param projectId
     * @param authRoleId
     * @param check
     */
    public void checkProjectStatus(Long projectId, Integer authRoleId, boolean check) {
        //开启校验，或者用户角色权限比访客高
        if (check || authRoleId < ProjectRoleAuthEnum.VISITOR.getValue()) {
            String key = String.format(ProjectConstant.PROJECT_STATUS_KEY, projectId);
            ProjectStatusDTO psd = (ProjectStatusDTO) redisUtil.get(key);
            if (psd == null) {
                psd = projectService.getProjectStatus(projectId);
                if (psd == null) {
                    throw DataScienceException
                        .of(BaseErrorCode.PROJECT_NOT_EXIST, "项目id:" + projectId);
                } else {
                    redisUtil.set(key, psd, 3600);
                }
            }
            //权限比访客高且项目是模板项目，如果是模板项目，所有更新、删除操作都不能执行
            if (authRoleId < ProjectRoleAuthEnum.VISITOR.getValue() && psd.getType() == 1) {
                throw DataScienceException
                    .of(BaseErrorCode.PROJECT_IS_TEMPLATE, "项目id:" + projectId);
            }

            if (check && psd.getLock() == 1) {
                throw DataScienceException.of(BaseErrorCode.PROJECT_LOCKED, "项目id:" + projectId);
            }
        }
    }

    public void checkProjectStatus(ProjectStatusDTO psd, Integer authRoleId, boolean checkLock) {
        //用户角色权限比访客高的操作
        if (checkLock || authRoleId < ProjectRoleAuthEnum.VISITOR.getValue()) {
            //模板项目，所有更新、删除操作只有创建者可以操作
            if (!authRoleId.equals(ProjectRoleAuthEnum.CREATOR.getValue()) && psd.getType() == 1) {
                throw DataScienceException
                    .of(BaseErrorCode.PROJECT_IS_TEMPLATE, "项目id:" + psd.getId());
            }

            //lock校验
            if (checkLock && psd.getLock() == 1) {
                throw DataScienceException.of(BaseErrorCode.PROJECT_LOCKED, "项目id:" + psd.getId());
            }
        }
    }

    /**
     * 获取当前用户在指定项目中发角色信息
     *
     * @param projectId
     * @return
     */
    public Object getUserRole(Long projectId) {
        long userId = JwtUtil.getCurrentUserId();
        Integer roleId = userProjectMapper.getRoleId(userId, projectId);
        if (roleId == null || roleId > 4) {
            ProjectDTO p = projectService.query(projectId);
            //模板项目，所有非创建者用户都是访客
            if (p.getType() == 1) {
                roleId = 4;
            } else {
                return null;
            }
        }

        RoleDTO role = roleMapper.getRoleById(roleId);

        return role;
    }

    /**
     * 检查用户在项目中加载数据情况
     *
     * @param projectId
     * @param authUserId
     */
    public void checkDatasetUsedInProject(Long projectId, Long authUserId) {
        List<DatasetProjectDTO> dps = datasetProjectService
            .selectCountByProjectAndUser(projectId, authUserId);
        if (dps == null || dps.isEmpty()) {
            return;
        } else {
            Set<Long> ids = new HashSet<>();
            for (DatasetProjectDTO dp : dps) {
                ids.add(dp.getProjectId());
            }
            List<ProjectDTO> ps = projectService.queryByIds(ids);
            StringBuilder sb = new StringBuilder();
            for (ProjectDTO p : ps) {
                sb.append(p.getName()).append(",");
            }
            throw DataScienceException.of(BaseErrorCode.DATASET_USED_IN_PROJECT,
                "已载入项目：" + (sb.length() > 0 ? sb.deleteCharAt(sb.length() - 1).toString() : ""));
        }
    }

    public Set<Long> getReceiver(Long projectId, Set<Long> removeUser, Set<Integer> removeRole) {
        List<UserProjectDTO> res = query(projectId);
        if (res == null) {
            return null;
        }
        Set<Long> receivers = new HashSet<>();
        for (UserProjectDTO userProject : res) {
            userProject.getRoleId();
            if (removeRole != null && removeRole.contains(userProject.getRoleId())
                || (removeUser != null && removeUser.contains(userProject.getUserId()))) {
                continue;
            }
            receivers.add(userProject.getUserId());
        }
        return receivers;
    }

    public JSONObject userDatasetUsedCheck(UserProjectVO vo) {
        JSONObject res = new JSONObject();
        res.put("otherUsersAreUsing",
            datasetProjectService.userDatasetUsedCheck(vo.getProjectId(), vo.getUserId()));
        return res;
    }
}
